#include <kutils.h>
#include "irq.h"
#include "idt.h"
#include "display.h"

void irq_remap(void);
void irq_handler(struct regs *r);

extern void irq0(void);
extern void irq1(void);
extern void irq2(void);
extern void irq3(void);
extern void irq4(void);
extern void irq5(void);
extern void irq6(void);
extern void irq7(void);
extern void irq8(void);
extern void irq9(void);
extern void irq10(void);
extern void irq11(void);
extern void irq12(void);
extern void irq13(void);
extern void irq14(void);
extern void irq15(void);

void (*irq_routines[256])(struct regs *r);

void irq_install_handler(s32int irq, void (*handler)(struct regs *r))
{
    irq_routines[irq] = handler;
}

void irq_uninstall_handler(s32int irq)
{
    irq_routines[irq] = 0;
}

// make IRQ0 to 15 be remapped to IDT entries 32 to 47 
void irq_remap(void)
{
    outportb(0x20, 0x11);
    outportb(0xA0, 0x11);
    outportb(0x21, 0x20);
    outportb(0xA1, 0x28);
    outportb(0x21, 0x04);
    outportb(0xA1, 0x02);
    outportb(0x21, 0x01);
    outportb(0xA1, 0x01);
    outportb(0x21, 0x0);
    outportb(0xA1, 0x0);
}

void irq_install(void)
{
#ifdef FLUIDIUM_DEBUG
	write_string("IRQ: Init\n");
#endif
   irq_remap();

   idt_set_gate(32, (unsigned)irq0, 0x08, 0x8E);
   idt_set_gate(33, (unsigned)irq1, 0x08, 0x8E);
	idt_set_gate(34, (unsigned)irq2, 0x08, 0x8E);
	idt_set_gate(35, (unsigned)irq3, 0x08, 0x8E);
	idt_set_gate(36, (unsigned)irq4, 0x08, 0x8E);
	idt_set_gate(37, (unsigned)irq5, 0x08, 0x8E);
	idt_set_gate(38, (unsigned)irq6, 0x08, 0x8E);
	idt_set_gate(39, (unsigned)irq7, 0x08, 0x8E);
	idt_set_gate(40, (unsigned)irq8, 0x08, 0x8E);
	idt_set_gate(41, (unsigned)irq9, 0x08, 0x8E);
	idt_set_gate(42, (unsigned)irq10, 0x08, 0x8E);
	idt_set_gate(43, (unsigned)irq11, 0x08, 0x8E);
	idt_set_gate(44, (unsigned)irq12, 0x08, 0x8E);
	idt_set_gate(45, (unsigned)irq13, 0x08, 0x8E);
	idt_set_gate(46, (unsigned)irq14, 0x08, 0x8E);
   idt_set_gate(47, (unsigned)irq15, 0x08, 0x8E);
}

void irq_handler(struct regs *r)
{
	void (*handler)(struct regs *r);

	//possibly send an eoi to the slave controller too
	if (r->int_no >= 40)
	{
		outportb(0xA0, 0x20);
	}
	//same for the master controller
	outportb(0x20, 0x20);

	if(r->int_no < 48)
	{
    	handler = irq_routines[(u8int)(r->int_no - 32)];
	}
	else
	{
		handler = irq_routines[(u8int)r->int_no];
	}
   if (handler)
   {
       handler(r);
   }
}
